Log why request-handler unregistration fails during teardown - #2573
Conversation
At app quit, UnsubscriberAsyncList logs a bare "Unsubscriber at index N failed!" with no context when a registerCommand unregistration resolves false instead of throwing. Add logger.warn calls at each silent false-returning branch (jsonRpc unset in the network.service closure; method not locally registered or remote UNREGISTER_METHOD round-trip failed in rpc-client's unregisterMethod) so the next occurrence self-describes. No behavior change — return values, ordering, and error propagation are identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fb6f3f0 to
105fbb4
Compare
Code reviewFound 1 issue:
paranext-core/src/shared/services/network.service.ts Lines 319 to 327 in 105fbb4 Documented expected-quiet-shutdown intent it contradicts: paranext-core/src/shared/services/network.service.ts Lines 112 to 115 in 105fbb4 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
… of warn Addresses lyonsil's #2573 review: the !jsonRpc branch is the normal graceful-shutdown path (shutdown() clears jsonRpc before disposing emitters), so warning there fires on every quit. Drop to debug; keep warn for the genuine unregistered === false case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
🤖 Claude: Good catch — agreed. The |
lyonsil
left a comment
There was a problem hiding this comment.
@lyonsil reviewed 2 files and all commit messages.
Reviewable status:complete! all files reviewed, all discussions resolved.
Summary
At every app quit, the log shows an anonymous failure with zero context:
Diagnosis: the failing unsubscriber is a
registerCommandunregistration thatresolves
falserather than throwing, and nothing in the chain logs why.The chain:
src/shared/services/network.service.ts— the unsubscriber closure returnedby
registerRequestHandlerreturnsfalseifjsonRpcis unset, or passesthrough whatever
jsonRpc.unregisterMethod(requestType)resolves.src/client/services/rpc-client.tsunregisterMethodhas three silentfalse-returning branches: the method isn't locally registered (checkedtwice, once before and once after acquiring the mutex), or the remote
UNREGISTER_METHODround-trip itself resolvedfalse.None of these branches logged anything, so
UnsubscriberAsyncListonly eversaw an opaque
falsewith no way to tell which branch fired or for whichrequest/method.
Changes
Added one
logger.warnper silent branch, each naming the request/methodtype so the log line is actionable:
network.service.ts: warn when the closure returnsfalsebecausejsonRpcis unset, and warn ifjsonRpc.unregisterMethoditself resolvedfalse.rpc-client.tsunregisterMethod: warn (matching this file's existinglogger.warnconvention for the analogousregisterMethod"alreadyregistered" branches) distinguishing "not locally registered" from "remote
unregister round-trip failed", each naming the
methodName.Behavior is unchanged — return values, ordering, and error propagation
are identical; this is purely additive logging. The underlying failure is
pre-existing and cosmetic (nothing depends on the unsubscriber succeeding at
quit-time); this PR just makes the next occurrence self-describing instead of
an anonymous index number.
Test plan
eslinton both changed files — cleantsc -p ./tsconfig.json --noEmit— no new errors (the one pre-existingerror, a missing generated
release/app/buildInfo.json, reproducesidentically on unmodified
mainand is unrelated to these files)vitest run src/shared/services/__tests__/network.service.shared-events.test.ts— 10/10 passing (no dedicated unit tests exist for
rpc-client.ts)🤖 Generated with Claude Code
Self-review — 2026-07-17 (review-paratext methodology)
Approve as-is — clean, low-risk, additive-only diagnostic logging; no behavior change. Four analysis passes (api / style / compliance / ux) per
.claude/agents/review-analyzer.md, run by a review-lead agent; author interview substituted with the PR body + PRD design records; every finding adversarially verified against code and tests.unregisterMethod's pre-mutex fast-path and post-mutex re-check log the identical message, so a log line alone can't distinguish "already gone" from "lost the race"registerMethodprecedent)Investigated separately: the "could this spam every shutdown" concern is unfounded — the new
warnlogs only fire on paths where two more-severe, pre-existing logs (console.errorinUnsubscriberAsyncList,logger.errorindeactivateExtension) already fire today.Gates:
prettier --checkpass,tsc --noEmit0 errors,eslint0 errors/warnings,vitest network.service.shared-events.test.ts10/10 pass.This change is